package com.tomecode.tools.maven.settings;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.dom4j.Comment;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import com.tomecode.tools.maven.settings.model.Activation;
import com.tomecode.tools.maven.settings.model.ChecksumPolicy;
import com.tomecode.tools.maven.settings.model.Layout;
import com.tomecode.tools.maven.settings.model.Mirror;
import com.tomecode.tools.maven.settings.model.Os;
import com.tomecode.tools.maven.settings.model.PluginRepository;
import com.tomecode.tools.maven.settings.model.Profile;
import com.tomecode.tools.maven.settings.model.Property;
import com.tomecode.tools.maven.settings.model.Proxy;
import com.tomecode.tools.maven.settings.model.Release;
import com.tomecode.tools.maven.settings.model.Repository;
import com.tomecode.tools.maven.settings.model.Server;
import com.tomecode.tools.maven.settings.model.Settings;
import com.tomecode.tools.maven.settings.model.Snapshot;
import com.tomecode.tools.maven.settings.model.UpdatePolicy;
import com.tomecode.tools.maven.settings.util.Backup;

/**
 * read/edit/save settings.xml
 * 
 * @author Frastia Tomas
 * 
 */
public final class SettingsFactory {

	private static final String M_SETTINGS_XML = "maven.settings.xml";
	private static final String M_REPOSITORY = "maven.repository";

	private static final String E_SETTINGS = "settings";
	private static final String E_SERVER = "server";
	private static final String E_SERVERS = E_SERVER + "s";
	private static final String E_ID = "id";
	private static final String E_USERNAME = "username";
	private static final String E_PASSWORD = "password";

	private static final String E_MIRROR = "mirror";
	private static final String E_MIRRORS = E_MIRROR + "s";
	private static final String E_MIRROROF = "mirrorOf";
	private static final String E_URL = "url";

	private static final String E_PROFILE = "profile";
	private static final String E_PROFILES = E_PROFILE + "s";

	private static final String E_REPOSITORIES = "repositories";

	private static final String E_REPOSITORY = "repository";
	private static final String E_RELEASES = "releases";
	private static final String E_LAYOUT = "layout";
	private static final String E_ENABLED = "enabled";
	private static final String E_SNAPSHOTS = "snapshots";

	private static final String E_PLUGIN_REPOSITORIES = "pluginRepositories";
	private static final String E_PLUGIN_REPOSITORY = "pluginRepository";

	private static final String E_ACTIVE_PROFILE = "activeProfile";
	private static final String E_ACTIVE_PROFILES = E_ACTIVE_PROFILE + "s";

	public static final String E_LOCAL_REPOSITORY = "localRepository";

	public static final String E_INTERACTIVE_MODE = "interactiveMode";

	public static final String E_USE_PLUGIN_REGISTRY = "usePluginRegistry";
	public static final String E_OFFLINE = "offline";

	private static final String E_PROXIES = "proxies";
	private static final String E_PROXY = "proxy";

	private static final String E_NAME = "name";
	private static final String E_ACTIVE = "active";
	private static final String E_PROTOCOL = "protocol";
	private static final String E_HOST = "host";
	private static final String E_PORT = "port";
	private static final String E_NONPROXYHOSTS = "nonProxyHosts";

	private static final String E_PRIVATE_KEY = "privateKey";
	private static final String E_PASSPHRASE = "passphrase";
	private static final String E_FILE_PERMISSIONS = "filePermissions";
	private static final String E_DIRECTORY_PERMISSIONS = "directoryPermissions";
	private static final String E_CONFIGURATION = "configuration";

	private static final String E_ACTIVATION = "activation";
	private static final String E_ACTIVE_BY_DEFAULT = "activeByDefault";
	private static final String E_JDK = "jdk";
	private static final String E_OS = "os";

	private static final String E_FAMILY = "family";
	private static final String E_ARCH = "arch";
	private static final String E_VERSION = "version";

	private static final String E_FILE = "file";
	private static final String E_EXISTS = "exists";
	private static final String E_MISSING = "missing";

	private static final String E_PROPERTY = "property";
	private static final String E_VALUE = "value";

	private static final String E_PROPERTIES = "properties";
	private static final String E_CHECKSUMPOLICY = "checksumPolicy";
	private static final String E_UPDATEPOLICY = "updatePolicy";

	private static final String E_PLUGINGROUP = "pluginGroup";
	private static final String E_PLUGINGROUPS = E_PLUGINGROUP + "s";

	/**
	 * load setting xml if not found then trow exception
	 * 
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	public static final Settings loadSettings() throws DocumentException, IOException {
		File file = new File(System.getProperty("user.home") + File.separator + "mswitch");
		if (!file.exists()) {
			if (!file.createNewFile()) {
				throw new IOException("Failed create mswitch config file: " + file);
			}
		}

		Properties properties = new Properties();
		properties.load(new FileInputStream(file));

		String settingsXml = properties.getProperty(M_SETTINGS_XML);
		if (isEmpty(settingsXml)) {
			throw new FileNotFoundException("not found maven settings.xml");
		}
		String repository = properties.getProperty(M_REPOSITORY);
		if (isEmpty(repository)) {
			throw new FileNotFoundException("not found maven repository folder");
		}
		return load(new Settings(new File(settingsXml), new File(repository)));
	}

	// /**
	// * get settingx.xml path from tmp mswitch file
	// *
	// * @param mswitch
	// * @return
	// * @throws IOException
	// */
	// private static final File getFile(File mswitch) throws IOException {
	// BufferedReader br = null;
	// try {
	// br = new BufferedReader(new FileReader(mswitch));
	// String line = null;
	// while ((line = br.readLine()) != null) {
	// File file = new File(line);
	// if (file.exists()) {
	// return file;
	// }
	// throw new FileNotFoundException("not found maven settings.xml:" + file);
	// }
	// } finally {
	// if (br != null) {
	// br.close();
	// }
	// }
	// throw new FileNotFoundException("not found maven settings.xml");
	// }

	/**
	 * start parse settingx.xml
	 * 
	 * @param file
	 * @return
	 * @throws DocumentException
	 * @throws FileNotFoundException
	 */
	public static final Settings load(Settings settings) throws DocumentException, FileNotFoundException {
		Document document = new SAXReader().read(settings.getFile());
		if (document == null) {
			throw new FileNotFoundException("not found maven settings.xml");
		}
		Element element = document.getRootElement();
		if (!element.getName().equals(E_SETTINGS)) {
			throw new IllegalArgumentException("not found element[" + E_SETTINGS + "]");
		}
		// set all namespaces
		// settings.setNamespaces(loadNamespaces(element));
		// settings.setNamespaceAttibutes(loadNamespacesAttr(element));
		settings.setServers(loadServers(element.element(E_SERVERS)));
		settings.setMirrors(loadMirrors(element.element(E_MIRRORS)));
		settings.setProfiles(loadProfiles(element.element(E_PROFILES)));
		settings.setActiveProfiles(loadActiveProfiles(element.element(E_ACTIVE_PROFILES)));
		settings.setLocalRepository(loadElementValue(element.element(E_LOCAL_REPOSITORY)));
		settings.setInteractiveMode(loadElementValue(element.element(E_INTERACTIVE_MODE)));
		settings.setUsePluginRegistry(loadElementValue(element.element(E_USE_PLUGIN_REGISTRY)));

		settings.setOffline(loadElementValue(element.element(E_OFFLINE)));
		settings.setProxies(loadProxies(element.element(E_PROXIES)));
		settings.setPluginGroups(loadPluginGroups(element.element(E_PLUGINGROUPS)));

		element.setParent(null);
		element.clearContent();
		settings.setOrginalElement(element);
		return settings;
	}

	/**
	 * load plugin groups
	 * 
	 * @param root
	 * @return
	 */
	private static final List<String> loadPluginGroups(Element root) {
		if (root == null) {
			return new ArrayList<String>();
		}
		List<?> elemenets = root.elements(E_PLUGINGROUP);
		List<String> pluginGroups = new ArrayList<String>();
		for (Object e : elemenets) {
			pluginGroups.add(((Element) e).getText());
		}

		return pluginGroups;
	}

	/**
	 * load list of {@link Proxy}
	 * 
	 * @param root
	 * @return
	 */
	private static final List<Proxy> loadProxies(Element root) {
		if (root == null) {
			return new ArrayList<Proxy>();
		}
		List<?> elemenets = root.elements(E_PROXY);
		List<Proxy> proxies = new ArrayList<Proxy>();
		for (Object e : elemenets) {
			proxies.add(loadProxy((Element) e));
		}

		return proxies;
	}

	/**
	 * load {@link Proxy}
	 * 
	 * @param e
	 * @return
	 */
	private static final Proxy loadProxy(Element e) {
		Proxy proxy = new Proxy();
		proxy.setId(e.elementText(E_ID));
		proxy.setActive(e.elementText(E_ACTIVE));
		proxy.setProtocol(e.elementText(E_PROTOCOL));
		proxy.setHost(e.elementText(E_HOST));
		proxy.setPort(e.elementText(E_PORT));
		proxy.setUsername(e.elementText(E_USERNAME));
		proxy.setPassword(e.elementText(E_PASSWORD));
		proxy.setNonProxyHosts(parserNonProxyHosts(e.elementText(E_NONPROXYHOSTS)));
		return proxy;
	}

	/**
	 * split non proxy hosts
	 * 
	 * @param nonProxyHosts
	 * @return
	 */
	private static final List<String> parserNonProxyHosts(String nonProxyHosts) {
		if (nonProxyHosts == null) {
			return new ArrayList<String>();
		}
		if (!nonProxyHosts.contains("|")) {
			return Arrays.asList(nonProxyHosts);
		}
		return Arrays.asList(nonProxyHosts.split("|"));
	}

	/**
	 * load {@link Element} text value
	 * 
	 * @param element
	 * @return
	 */
	private static String loadElementValue(Element element) {
		if (element == null) {
			return null;
		}
		return element.getText();
	}

	/**
	 * load all activated profiles
	 * 
	 * @param root
	 * @return
	 */
	private static final List<String> loadActiveProfiles(Element root) {
		if (root == null) {
			return new ArrayList<String>();
		}

		List<?> elemenets = root.elements(E_ACTIVE_PROFILE);
		List<String> profiles = new ArrayList<String>();
		for (Object e : elemenets) {
			profiles.add(((Element) e).getText());
		}

		return profiles;
	}

	/**
	 * load list of {@link Profile}
	 * 
	 * @param root
	 * @return
	 */
	private static final List<Profile> loadProfiles(Element root) {
		if (root == null) {
			return new ArrayList<Profile>();
		}

		List<?> elemenets = root.elements(E_PROFILE);
		List<Profile> profiles = new ArrayList<Profile>();
		for (Object e : elemenets) {
			profiles.add(loadProfile((Element) e));
		}

		return profiles;
	}

	/**
	 * load {@link Profile}
	 * 
	 * @param e
	 * @return
	 */
	private static final Profile loadProfile(Element e) {
		Profile profile = new Profile();
		profile.setId(e.elementText(E_ID));
		profile.setRepositories(loadRepositories(e.element(E_REPOSITORIES)));
		profile.setPluginRepositories(loadPluginRepositories(e.element(E_PLUGIN_REPOSITORIES)));
		profile.setActivation(loadActivation(e.element(E_ACTIVATION)));
		loadProperties(e.elementIterator(E_PROPERTIES), profile);
		return profile;
	}

	/**
	 * load all properties
	 * 
	 * @param es
	 * @param profile
	 */
	private static final void loadProperties(Iterator<?> es, Profile profile) {
		while (es.hasNext()) {
			Element e = (Element) es.next();
			profile.addProperty(e.getName(), e.getText());
		}
	}

	/**
	 * load {@link Activation}
	 * 
	 * @param e
	 * @return
	 */
	private static final Activation loadActivation(Element e) {
		if (e == null) {
			return null;
		}

		Activation activation = new Activation();
		activation.setActiveByDefault(e.elementText(E_ACTIVE_BY_DEFAULT));
		activation.setJdk(e.elementText(E_JDK));
		activation.setOs(loadOs(e.element(E_OS)));
		activation.setProperty(loadProperty(e.element(E_PROPERTY)));
		activation.setFile(loadFile(e.element(E_FILE)));
		return activation;
	}

	/**
	 * load {@link Property}
	 * 
	 * @param e
	 * @return
	 */
	private static final Property loadProperty(Element e) {
		if (e == null) {
			return null;
		}
		Property property = new Property();
		property.setName(e.elementText(E_NAME));
		property.setValue(e.elementText(E_VALUE));
		return property;
	}

	/**
	 * load {@link com.tomecode.tools.maven.settings.model.File}
	 * 
	 * @param e
	 * @return
	 */
	private static final com.tomecode.tools.maven.settings.model.File loadFile(Element e) {
		if (e == null) {
			return null;
		}
		com.tomecode.tools.maven.settings.model.File file = new com.tomecode.tools.maven.settings.model.File();
		file.setExists(e.elementText(E_EXISTS));
		file.setMissing(e.elementText(E_MISSING));
		return file;
	}

	/**
	 * load {@link Os}
	 * 
	 * @param e
	 * @return
	 */
	private static final Os loadOs(Element e) {
		if (e == null) {
			return null;
		}
		Os os = new Os();
		os.setName(e.elementText(E_NAME));
		os.setFamily(e.elementText(E_FAMILY));
		os.setArch(e.elementText(E_ARCH));
		os.setVersion(e.elementText(E_VERSION));
		return os;
	}

	/**
	 * load all {@link Repository}
	 * 
	 * @param root
	 * @return
	 */
	private static final List<Repository> loadRepositories(Element root) {
		if (root == null) {
			return new ArrayList<Repository>();
		}

		List<?> elemenets = root.elements(E_REPOSITORY);
		List<Repository> repositories = new ArrayList<Repository>();
		for (Object e : elemenets) {
			repositories.add(loadRepository((Element) e));
		}

		return repositories;
	}

	/**
	 * load all {@link PluginRepository}
	 * 
	 * @param root
	 * @return
	 */
	private static final List<PluginRepository> loadPluginRepositories(Element root) {
		if (root == null) {
			return new ArrayList<PluginRepository>();
		}

		List<?> elemenets = root.elements(E_PLUGIN_REPOSITORY);
		List<PluginRepository> pluginRepositories = new ArrayList<PluginRepository>();
		for (Object e : elemenets) {
			pluginRepositories.add(loadPluginRepository((Element) e));
		}

		return pluginRepositories;
	}

	/**
	 * load {@link PluginRepository}
	 * 
	 * @param e
	 * @return
	 */
	private static final PluginRepository loadPluginRepository(Element e) {
		PluginRepository repository = new PluginRepository();
		repository.setId(e.elementText(E_ID));
		repository.setUrl(e.elementText(E_URL));
		repository.setReleases(loadReleases(e.element(E_RELEASES)));
		repository.setSnapshots(loadSnapshots(e.element(E_SNAPSHOTS)));
		return repository;
	}

	/**
	 * load {@link Repository}
	 * 
	 * @param e
	 * @return
	 */
	private static final Repository loadRepository(Element e) {
		Repository repository = new Repository();
		repository.setId(e.elementText(E_ID));
		repository.setUrl(e.elementText(E_URL));
		repository.setName(e.elementText(E_NAME));
		repository.setLayout(e.elementText(E_LAYOUT));
		repository.setReleases(loadReleases(e.element(E_RELEASES)));
		repository.setSnapshots(loadSnapshots(e.element(E_SNAPSHOTS)));
		return repository;
	}

	/**
	 * load {@link Release}
	 * 
	 * @param root
	 * @return
	 */
	private static final Release loadReleases(Element root) {
		if (root == null) {
			return null;
		}
		Release release = new Release();
		release.setEnabled(root.elementText(E_ENABLED));
		release.setChecksumPolicy(root.elementText(E_CHECKSUMPOLICY));
		release.setUpdatePolicy(root.elementText(E_UPDATEPOLICY));
		return release;
	}

	/**
	 * load {@link Snapshot}
	 * 
	 * @param root
	 * @return
	 */
	private static final Snapshot loadSnapshots(Element root) {
		if (root == null) {
			return null;
		}
		Snapshot snapshot = new Snapshot();
		snapshot.setEnabled(root.elementText(E_ENABLED));
		snapshot.setChecksumPolicy(root.elementText(E_CHECKSUMPOLICY));
		snapshot.setUpdatePolicy(root.elementText(E_UPDATEPOLICY));
		return snapshot;
	}

	/**
	 * load all servers
	 * 
	 * @param root
	 * @return
	 */
	private final static List<Server> loadServers(Element root) {
		if (root == null) {
			return new ArrayList<Server>();
		}
		List<?> elemenets = root.elements(E_SERVER);
		List<Server> servers = new ArrayList<Server>();
		for (Object e : elemenets) {
			servers.add(loadServer((Element) e));
		}

		return servers;
	}

	/**
	 * load {@link Mirror}
	 * 
	 * @param root
	 * @return
	 */
	private final static List<Mirror> loadMirrors(Element root) {
		if (root == null) {
			return new ArrayList<Mirror>();
		}
		List<?> elemenets = root.elements(E_MIRROR);
		List<Mirror> mirrors = new ArrayList<Mirror>();
		for (Object e : elemenets) {
			mirrors.add(loadMirror((Element) e));
		}

		return mirrors;
	}

	private static final Mirror loadMirror(Element e) {
		Mirror mirror = new Mirror();
		for (Object o : e.content()) {
			if (o instanceof Comment) {
				o.toString();
			} else if (o instanceof Element) {
				o.toString();
			}
		}

		mirror.setId(e.elementText(E_ID));
		mirror.setName(e.elementText(E_NAME));
		mirror.setMirrorOf(e.elementText(E_MIRROROF));
		mirror.setUrl(e.elementText(E_URL));
		return mirror;
	}

	/**
	 * parser server
	 * 
	 * @param e
	 * @return
	 */
	private static final Server loadServer(Element e) {
		Server server = new Server();
		server.setId(e.elementText(E_ID));
		server.setUsername(e.elementText(E_USERNAME));
		server.setPassword(e.elementText(E_PASSWORD));
		server.setPrivateKey(e.elementText(E_PRIVATE_KEY));
		server.setPassphrase(e.elementText(E_PASSPHRASE));
		server.setFilePermissions(e.elementText(E_FILE_PERMISSIONS));
		server.setDirectoryPermissions(e.elementText(E_DIRECTORY_PERMISSIONS));
		server.setConfiguration(e.elementText(E_CONFIGURATION));
		return server;
	}

	public static final void saveSettingFile(File settingsXml, File repository) throws IOException {
		File file = new File(System.getProperty("user.home") + File.separator + "mswitch");
		if (!file.exists()) {
			file.createNewFile();
		}

		Properties properties = new Properties();
		properties.load(new FileInputStream(file));

		properties.put(M_SETTINGS_XML, settingsXml.getPath());
		properties.put(M_REPOSITORY, repository.getPath());

		properties.store(new FileOutputStream(file), null);

		// BufferedWriter writer = null;
		// try {
		// writer = new BufferedWriter(new FileWriter(file));
		// writer.write(settingsXml.getPath());
		// writer.newLine();
		// } finally {
		// if (writer != null) {
		// writer.close();
		// }
		// }

	}

	/**
	 * save changes in {@link Settings} to settings.xml
	 * 
	 * @param settings
	 * @throws IOException
	 */
	public final static void save(Settings settings) throws IOException {
		Document document = createSettingsDoc(settings);

		FileOutputStream fos = new FileOutputStream(settings.getFile());
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setSuppressDeclaration(true);
		XMLWriter writer = new XMLWriter(fos, format);
		try {
			writer.write(document);
			writer.flush();

		} finally {
			writer.close();
		}
	}

	/**
	 * create {@link Settings} xml document
	 * 
	 * @param settings
	 * @return
	 */
	private static final Document createSettingsDoc(Settings settings) {
		Document doc = DocumentFactory.getInstance().createDocument();
		doc.setXMLEncoding("UTF-8");

		Element eSettings = settings.getOrginalElement();
		eSettings.setDocument(doc);
		eSettings.clearContent();

		createSettings(settings, eSettings);
		createProxies(settings.getProxies(), eSettings);
		createServers(settings.getServers(), eSettings);
		createMirros(settings.getMirrors(), eSettings);
		createProfiles(settings.getProfiles(), eSettings);
		createActiveProfiles(settings.getActiveProfiles(), eSettings);
		createPluginGroups(settings.getPluginGroups(), eSettings);
		doc.setRootElement(eSettings);
		return doc;
	}

	/**
	 * create elements: {@value #E_LOCAL_REPOSITORY},
	 * {@link #E_INTERACTIVE_MODE}, {@link #E_OFFLINE} ,
	 * {@link #E_USE_PLUGIN_REGISTRY}
	 * 
	 * @param settings
	 * @param eSettings
	 */
	private static final void createSettings(Settings settings, Element eSettings) {
		if (!settings.isDeleted(E_LOCAL_REPOSITORY) && settings.getLocalRepository() != null) {
			eSettings.add(createElement(E_LOCAL_REPOSITORY, settings.getLocalRepository(), eSettings.getNamespace()));
		}
		if (!settings.isDeleted(E_INTERACTIVE_MODE)) {
			eSettings.add(createElement(E_INTERACTIVE_MODE, String.valueOf(settings.isInteractiveMode()), eSettings.getNamespace()));
		}
		if (!settings.isDeleted(E_OFFLINE)) {
			eSettings.add(createElement(E_OFFLINE, String.valueOf(settings.isOffline()), eSettings.getNamespace()));
		}
		if (!settings.isDeleted(E_USE_PLUGIN_REGISTRY)) {
			eSettings.add(createElement(E_USE_PLUGIN_REGISTRY, String.valueOf(settings.isUsePluginRegistry()), eSettings.getNamespace()));
		}
	}

	/**
	 * create proxies element
	 * 
	 * @param proxies
	 * @param settings
	 */
	private static final void createProxies(List<Proxy> proxies, Element eSettings) {
		if (proxies.isEmpty()) {
			return;
		}

		Element eProxies = DocumentHelper.createElement(new QName(E_PROXIES, eSettings.getNamespace()));
		for (Proxy proxy : proxies) {
			eProxies.add(createProxy(proxy, eSettings.getNamespace()));
		}
		eSettings.add(eProxies);
	}

	/**
	 * create {@link Proxy}
	 * 
	 * @param proxy
	 * @param namespace
	 * @return
	 */
	private static final Element createProxy(Proxy proxy, Namespace namespace) {
		Element eProxy = DocumentHelper.createElement(new QName(E_PROXY, namespace));

		if (!isEmpty(proxy.getActive())) {
			eProxy.add(createElement(E_ACTIVE, proxy.getActive(), eProxy.getNamespace()));
		}
		if (!isEmpty(proxy.getProtocol())) {
			eProxy.add(createElement(E_PROTOCOL, proxy.getProtocol(), eProxy.getNamespace()));
		}
		if (!isEmpty(proxy.getUsername())) {
			eProxy.add(createElement(E_USERNAME, proxy.getUsername(), eProxy.getNamespace()));
		}
		if (!isEmpty(proxy.getPassword())) {
			eProxy.add(createElement(E_PASSWORD, proxy.getPassword(), eProxy.getNamespace()));
		}
		if (!isEmpty(proxy.getPort())) {
			eProxy.add(createElement(E_PORT, proxy.getPort(), eProxy.getNamespace()));
		}
		if (!isEmpty(proxy.getHost())) {
			eProxy.add(createElement(E_HOST, proxy.getHost(), eProxy.getNamespace()));
		}

		if (!proxy.getNonProxyHosts().isEmpty()) {
			eProxy.add(createElement(E_NONPROXYHOSTS, proxy.asNonProxyHosts(), eProxy.getNamespace()));
		}
		if (!isEmpty(proxy.getId())) {
			eProxy.add(createElement(E_ID, proxy.getId(), eProxy.getNamespace()));
		}

		return eProxy;
	}

	/**
	 * create elements profiles
	 * 
	 * @param profiles
	 * @param eSettings
	 */
	private static final void createProfiles(List<Profile> profiles, Element eSettings) {
		if (profiles.isEmpty()) {
			return;
		}
		Element eProfiles = DocumentHelper.createElement(new QName(E_PROFILES, eSettings.getNamespace()));
		for (Profile profile : profiles) {
			eProfiles.add(createProfile(profile, eProfiles.getNamespace()));
		}
		eSettings.add(eProfiles);
	}

	/**
	 * creaate element {@link Profile}
	 * 
	 * @param profile
	 * @param namespace
	 * @return
	 */
	private static final Element createProfile(Profile profile, Namespace namespace) {
		Element eProfile = DocumentHelper.createElement(new QName(E_PROFILE, namespace));

		if (profile.getActivation() != null) {
			eProfile.add(createActivation(profile.getActivation(), eProfile.getNamespace()));
		}
		if (!profile.getProperties().isEmpty()) {
			eProfile.add(createProperties(profile.getProperties(), eProfile.getNamespace()));
		}

		if (!profile.getRepositories().isEmpty()) {
			eProfile.add(createRepositories(profile.getRepositories(), eProfile.getNamespace()));
		}

		if (!profile.getPluginRepositories().isEmpty()) {
			eProfile.add(createPluginRepositories(profile.getPluginRepositories(), eProfile.getNamespace()));
		}
		if (!isEmpty(profile.getId())) {
			eProfile.add(createElement(E_ID, profile.getId(), eProfile.getNamespace()));
		}
		return eProfile;
	}

	/**
	 * create element of {@link PluginRepository}
	 * 
	 * @param pluginRepositories
	 * @param namespace
	 */
	private final static Element createPluginRepositories(List<PluginRepository> pluginRepositories, Namespace namespace) {
		Element ePluginRepositories = DocumentHelper.createElement(new QName(E_PLUGIN_REPOSITORIES, namespace));
		for (PluginRepository pluginRepository : pluginRepositories) {
			ePluginRepositories.add(createPluginRepository(pluginRepository, ePluginRepositories.getNamespace()));
		}

		return ePluginRepositories;
	}

	/**
	 * create {@link PluginRepository}
	 * 
	 * @param pluginRepository
	 * @param namespace
	 * @return
	 */
	private static final Element createPluginRepository(PluginRepository pluginRepository, Namespace namespace) {
		Element ePluginRepository = DocumentHelper.createElement(new QName(E_PLUGIN_REPOSITORY, namespace));

		createReleases(ePluginRepository, pluginRepository.getReleases());
		createSnapshots(ePluginRepository, pluginRepository.getSnapshots());

		if (!isEmpty(pluginRepository.getId())) {
			ePluginRepository.add(createElement(E_ID, pluginRepository.getId(), ePluginRepository.getNamespace()));
		}
		if (!isEmpty(pluginRepository.getName())) {
			ePluginRepository.add(createElement(E_NAME, pluginRepository.getName(), ePluginRepository.getNamespace()));
		}
		if (!isEmpty(pluginRepository.getUrl())) {
			ePluginRepository.add(createElement(E_URL, pluginRepository.getUrl(), ePluginRepository.getNamespace()));
		}
		if (pluginRepository.getLayout() != null && pluginRepository.getLayout() != Layout.EMPTY) {
			ePluginRepository.add(createElement(E_LAYOUT, pluginRepository.getLayout().toString(), ePluginRepository.getNamespace()));
		}
		return ePluginRepository;
	}

	/**
	 * create element {@link Repository}ies
	 * 
	 * @param repositories
	 * @param namespace
	 * @return
	 */
	private static final Element createRepositories(List<Repository> repositories, Namespace namespace) {
		Element eRepositories = DocumentHelper.createElement(new QName(E_REPOSITORIES, namespace));
		for (Repository repository : repositories) {
			eRepositories.add(createRepository(repository, eRepositories.getNamespace()));
		}
		return eRepositories;
	}

	/**
	 * create element {@link Repository}
	 * 
	 * @param repository
	 * @param namespace
	 * @return
	 */
	private static final Element createRepository(Repository repository, Namespace namespace) {
		Element eRepository = DocumentHelper.createElement(new QName(E_REPOSITORY, namespace));
		createReleases(eRepository, repository.getReleases());
		createSnapshots(eRepository, repository.getSnapshots());

		if (!isEmpty(repository.getId())) {
			eRepository.add(createElement(E_ID, repository.getId(), eRepository.getNamespace()));
		}
		if (!isEmpty(repository.getName())) {
			eRepository.add(createElement(E_NAME, repository.getName(), eRepository.getNamespace()));
		}
		if (!isEmpty(repository.getUrl())) {
			eRepository.add(createElement(E_URL, repository.getUrl(), eRepository.getNamespace()));
		}
		if (repository.getLayout() != Layout.EMPTY) {
			eRepository.add(createElement(E_LAYOUT, repository.getLayout().toString(), eRepository.getNamespace()));
		}
		return eRepository;
	}

	/**
	 * create element {@link Snapshot}
	 * 
	 * @param eRepository
	 * @param releases
	 */
	private static final void createSnapshots(Element eRepository, Snapshot releases) {
		if (releases == null) {
			return;
		}
		Element eReleases = DocumentHelper.createElement(new QName(E_SNAPSHOTS, eRepository.getNamespace()));

		if (!isEmpty(releases.getEnabled())) {
			eReleases.add(createElement(E_ENABLED, releases.getEnabled(), eReleases.getNamespace()));
		}
		if (releases.getUpdatePolicy() != UpdatePolicy.EMPTY) {
			eReleases.add(createElement(E_UPDATEPOLICY, releases.makeUpdatePolicy(), eReleases.getNamespace()));
		}
		if (releases.getChecksumPolicy() != ChecksumPolicy.EMPTY) {
			eReleases.add(createElement(E_CHECKSUMPOLICY, releases.getChecksumPolicy().toString(), eReleases.getNamespace()));
		}
		eRepository.add(eReleases);
	}

	/**
	 * create element releases
	 * 
	 * @param releases
	 * @param namespace
	 */
	private static final void createReleases(Element eRepository, Release releases) {
		if (releases == null) {
			return;
		}
		Element eReleases = DocumentHelper.createElement(new QName(E_RELEASES, eRepository.getNamespace()));

		if (!isEmpty(releases.getEnabled())) {
			eReleases.add(createElement(E_ENABLED, releases.getEnabled(), eReleases.getNamespace()));
		}
		if (releases.getUpdatePolicy() != UpdatePolicy.EMPTY) {
			eReleases.add(createElement(E_UPDATEPOLICY, releases.makeUpdatePolicy(), eReleases.getNamespace()));
		}
		if (releases.getChecksumPolicy() != ChecksumPolicy.EMPTY) {
			eReleases.add(createElement(E_CHECKSUMPOLICY, releases.getChecksumPolicy().toString(), eReleases.getNamespace()));
		}
		eRepository.add(eReleases);
	}

	/**
	 * create Properties
	 * 
	 * @param properties
	 * @param namespace
	 * @return
	 */
	private static final Element createProperties(Properties properties, Namespace namespace) {
		Element eProperties = DocumentHelper.createElement(new QName(E_PROPERTIES, namespace));

		Enumeration<Object> keys = properties.keys();
		while (keys.hasMoreElements()) {
			Object key = keys.nextElement();
			Object value = properties.get(key);
			eProperties.add(createElement(key.toString(), value.toString(), eProperties.getNamespace()));
		}

		return eProperties;
	}

	/**
	 * create element {@link Activation}
	 * 
	 * @param activation
	 * @param namespace
	 * @return
	 */
	private static final Element createActivation(Activation activation, Namespace namespace) {
		Element eActivation = DocumentHelper.createElement(new QName(E_ACTIVATION, namespace));
		if (!isEmpty(activation.getActiveByDefault())) {
			eActivation.add(createElement(E_ACTIVE_BY_DEFAULT, activation.getActiveByDefault(), namespace));
		}
		if (!isEmpty(activation.getJdk())) {
			eActivation.add(createElement(E_JDK, activation.getJdk(), namespace));
		}
		if (activation.getOs() != null) {
			eActivation.add(createOs(activation.getOs(), namespace));
		}
		if (activation.getProperty() != null) {
			eActivation.add(createProperty(activation.getProperty(), namespace));
		}
		if (activation.getFile() != null) {
			eActivation.add(createFile(activation.getFile(), namespace));
		}
		return eActivation;
	}

	/**
	 * create {@link com.tomecode.tools.maven.settings.model.File} element
	 * 
	 * @param file
	 * @param namespace
	 * @return
	 */
	private static final Element createFile(com.tomecode.tools.maven.settings.model.File file, Namespace namespace) {
		Element eFile = DocumentHelper.createElement(new QName(E_FILE, namespace));
		if (!isEmpty(file.getMissing())) {
			eFile.add(createElement(E_MISSING, file.getMissing(), eFile.getNamespace()));
		}
		if (!isEmpty(file.getExists())) {
			eFile.add(createElement(E_EXISTS, file.getExists(), eFile.getNamespace()));
		}
		return eFile;
	}

	/**
	 * create element {@link Property}
	 * 
	 * @param property
	 * @param namespace
	 * @return
	 */
	private static final Element createProperty(Property property, Namespace namespace) {
		Element eProperty = DocumentHelper.createElement(new QName(E_PROPERTY, namespace));
		if (!isEmpty(property.getName())) {
			eProperty.add(createElement(E_NAME, property.getName(), eProperty.getNamespace()));
		}
		if (!isEmpty(property.getValue())) {
			eProperty.add(createElement(E_VALUE, property.getValue(), eProperty.getNamespace()));
		}
		return eProperty;
	}

	/**
	 * create element {@link Os}
	 * 
	 * @param os
	 * @param namespace
	 * @return
	 */
	private static final Element createOs(Os os, Namespace namespace) {
		Element eOs = DocumentHelper.createElement(new QName(E_OS, namespace));
		if (!isEmpty(os.getName())) {
			eOs.add(createElement(E_NAME, os.getName(), eOs.getNamespace()));
		}
		if (!isEmpty(os.getFamily())) {
			eOs.add(createElement(E_FAMILY, os.getFamily(), eOs.getNamespace()));
		}
		if (!isEmpty(os.getArch())) {
			eOs.add(createElement(E_ARCH, os.getArch(), eOs.getNamespace()));
		}
		if (!isEmpty(os.getVersion())) {
			eOs.add(createElement(E_VERSION, os.getVersion(), eOs.getNamespace()));
		}
		return eOs;
	}

	/**
	 * create pluginGroups element
	 * 
	 * @param pluginGroups
	 * @param settings
	 */
	private static final void createPluginGroups(List<String> pluginGroups, Element eSettings) {
		if (pluginGroups.isEmpty()) {
			return;
		}

		Element ePluginGoups = DocumentHelper.createElement(new QName(E_PLUGINGROUPS, eSettings.getNamespace()));
		for (String pluginGroup : pluginGroups) {
			ePluginGoups.add(createElement(E_PLUGINGROUP, pluginGroup, ePluginGoups.getNamespace()));
		}

		eSettings.add(ePluginGoups);
	}

	/**
	 * create {@link Mirror} elements
	 * 
	 * @param mirrors
	 * @param settings
	 */
	private static final void createMirros(List<Mirror> mirrors, Element eSettings) {
		if (mirrors.isEmpty()) {
			return;
		}

		Element eMirrors = DocumentHelper.createElement(new QName(E_MIRRORS, eSettings.getNamespace()));

		for (Mirror mirror : mirrors) {
			eMirrors.add(createMirror(mirror, eMirrors.getNamespace()));
		}
		eSettings.add(eMirrors);
	}

	/**
	 * create {@link Mirror} element
	 * 
	 * @param mirror
	 * @param namespace
	 * @return
	 */
	private static final Element createMirror(Mirror mirror, Namespace namespace) {
		Element eMirror = DocumentHelper.createElement(new QName(E_MIRROR, namespace));
		if (!isEmpty(mirror.getMirrorOf())) {
			eMirror.add(createElement(E_MIRROROF, mirror.getMirrorOf(), namespace));
		}
		if (!isEmpty(mirror.getName())) {
			eMirror.add(createElement(E_NAME, mirror.getName(), namespace));
		}
		if (!isEmpty(mirror.getUrl())) {
			eMirror.add(createElement(E_URL, mirror.getUrl(), namespace));
		}
		if (!isEmpty(mirror.getId())) {
			eMirror.add(createElement(E_ID, mirror.getId(), namespace));
		}
		return eMirror;
	}

	/**
	 * create activeProfile element
	 * 
	 * @param activeProfiles
	 * @param eSettings
	 */
	private static final void createActiveProfiles(List<String> activeProfiles, Element eSettings) {
		if (activeProfiles.isEmpty()) {
			return;
		}
		Element eActiveProfile = DocumentHelper.createElement(new QName(E_ACTIVE_PROFILES, eSettings.getNamespace()));
		for (String activeProfile : activeProfiles) {
			eActiveProfile.add(createElement(E_ACTIVE_PROFILE, activeProfile, eActiveProfile.getNamespace()));
		}

		eSettings.add(eActiveProfile);
	}

	/**
	 * create servers element and content
	 * 
	 * @param servers
	 * @param eSettings
	 */
	private static final void createServers(List<Server> servers, Element eSettings) {
		if (servers.isEmpty()) {
			return;
		}

		Element eServers = DocumentHelper.createElement(new QName(E_SERVERS, eSettings.getNamespace()));
		for (Server server : servers) {
			eServers.add(createServer(server, eServers.getNamespace()));
		}

		eSettings.add(eServers);
	}

	/**
	 * create {@link Server} element
	 * 
	 * @param server
	 * @return
	 */
	private final static Element createServer(Server server, Namespace namespace) {
		Element eServer = DocumentHelper.createElement(new QName(E_SERVER, namespace));
		if (!isEmpty(server.getUsername())) {
			eServer.add(createElement(E_USERNAME, server.getUsername(), eServer.getNamespace()));
		}
		if (!isEmpty(server.getPassword())) {
			eServer.add(createElement(E_PASSWORD, server.getPassword(), eServer.getNamespace()));
		}
		if (!isEmpty(server.getPrivateKey())) {
			eServer.add(createElement(E_PRIVATE_KEY, server.getPrivateKey(), eServer.getNamespace()));
		}
		if (!isEmpty(server.getPassphrase())) {
			eServer.add(createElement(E_PASSPHRASE, server.getPassphrase(), eServer.getNamespace()));
		}
		if (!isEmpty(server.getFilePermissions())) {
			eServer.add(createElement(E_FILE_PERMISSIONS, server.getFilePermissions(), eServer.getNamespace()));
		}
		if (!isEmpty(server.getDirectoryPermissions())) {
			eServer.add(createElement(E_DIRECTORY_PERMISSIONS, server.getDirectoryPermissions(), eServer.getNamespace()));
		}
		if (!isEmpty(server.getConfiguration())) {
			eServer.add(createElement(E_CONFIGURATION, server.getConfiguration(), eServer.getNamespace()));
		}
		if (!isEmpty(server.getId())) {
			eServer.add(createElement(E_ID, server.getId(), eServer.getNamespace()));
		}

		return eServer;
	}

	/**
	 * create element with text
	 * 
	 * @param qName
	 * @param value
	 * @return
	 */
	private static final Element createElement(String qName, String value, Namespace namespace) {
		Element element = DocumentHelper.createElement(new QName(qName, namespace));
		element.setText(value);
		return element;
	}

	public static final boolean isEmpty(String value) {
		return (value == null || value.trim().length() == 0);
	}

	/**
	 * create backup settings
	 * 
	 * @param settings
	 * @throws IOException
	 */
	public static final File backup(Settings settings) throws IOException {
		Document document = createSettingsDoc(settings);
		document.addComment("maven switch backup: " + new Date());
		File f = new File("c:\\settings.xml_backup_" + new Date().getTime());
		FileOutputStream fos = new FileOutputStream(f);
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setSuppressDeclaration(true);
		XMLWriter writer = new XMLWriter(fos, format);
		try {
			writer.write(document);
			writer.flush();
		} finally {
			writer.close();
		}

		return f;
	}

	/**
	 * load all backup files
	 * 
	 * @param dir
	 * @return
	 */
	public final static List<Backup> loadBackups(File dir) {
		List<Backup> backups = new ArrayList<Backup>();

		for (File file : dir.listFiles()) {
			if (file.getName().contains(".xml_backup_")) {
				backups.add(new Backup(file));
			}
		}

		return backups;
	}

	/**
	 * restore backup - backup copy to settings.xml
	 * 
	 * @param settingsXml
	 * @param backup
	 * @throws IOException
	 */
	public final static void restore(File settingsXml, File backup) throws IOException {
		FileInputStream from = null;
		FileOutputStream to = null;
		try {
			from = new FileInputStream(backup);
			to = new FileOutputStream(settingsXml);
			byte[] buffer = new byte[4096];
			int bytesRead;

			while ((bytesRead = from.read(buffer)) != -1)
				to.write(buffer, 0, bytesRead); // write
		} finally {
			if (from != null)
				try {
					from.close();
				} catch (IOException e) {
					;
				}
			if (to != null)
				try {
					to.close();
				} catch (IOException e) {
				}
		}
	}
}
